package dai.engine

import dai.core.base.*
import dai.core.base.camera.Camera
import dai.core.base.light.PointLight
import dai.core.base.loader.ObjLoader
import org.joml.Matrix4f
import org.joml.Vector3f
import org.lwjgl.glfw.GLFW
import org.lwjgl.glfw.GLFW.*
import org.lwjgl.opengl.GL11C
import org.lwjgl.opengl.GL30C


class LightRender {

    val shaderProgram = ShaderProgram()
    val transformation = Transformation()
    val FOV = Math.toRadians(60.0).toFloat()
    val NEAR = 0.01f
    val FAR = 1000f
    var widthf = 0f
    var heightf = 0f
    val tmpMatrix = Matrix4f()

    val ambientLight = Vector3f(0.2f, 0.2f, 0.2f)
    val pointLight = PointLight()

    fun init(window: Window) {

        widthf = window.width.toFloat()
        heightf = window.height.toFloat()

        // init shader
        val vertexSource = Utils.loadShader("/light1.vs.shader")
        val fragSource = Utils.loadShader("/light1.fs.shader")
        shaderProgram.initShader(vertexSource, fragSource)

        // vs uniform
        shaderProgram.createUniform("projectMatrix")
        shaderProgram.createUniform("modelViewMatrix")

        // fs uniform
        shaderProgram.createUniform("texture_sampler")
        shaderProgram.createUniform("ambientLight")
        shaderProgram.createUniform("specularPower")

        // material
        shaderProgram.createUniform("material.ambient")
        shaderProgram.createUniform("material.diffuse")
        shaderProgram.createUniform("material.specular")
        shaderProgram.createUniform("material.hasTexture")
        shaderProgram.createUniform("material.reflectance")

        // point light
        shaderProgram.createUniform("pointLight.color")
        shaderProgram.createUniform("pointLight.position")
        shaderProgram.createUniform("pointLight.intensity")
        shaderProgram.createUniform("pointLight.att.constant")
        shaderProgram.createUniform("pointLight.att.linear")
        shaderProgram.createUniform("pointLight.att.exponent")

        GL30C.glClearColor(0f, 0f, 0f, 1f)
        GL30C.glEnable(GL30C.GL_DEPTH_TEST)
    }

    fun render(items: List<GameItem>, camera: Camera) {

        shaderProgram.bind()
        val projectMatrix = transformation.projectMatrix(FOV, widthf, heightf, NEAR, FAR)
        shaderProgram.setUniform("projectMatrix", projectMatrix)
        shaderProgram.setUniform("ambientLight", ambientLight)
        val viewMatrix = transformation.viewMatrix(camera)

        // render items
        for (item in items) {
            val worldMatrix = transformation.worldMatrix(
                item.position,
                item.rotation,
                item.scale
            )
            tmpMatrix.set(viewMatrix).mul(worldMatrix)
            shaderProgram.setUniform("modelViewMatrix", tmpMatrix)

            // material
            shaderProgram.setUniform("material.ambient", item.mesh.material.ambientColor)
            shaderProgram.setUniform("material.diffuse", item.mesh.material.diffuseColor)
            shaderProgram.setUniform("material.specular", item.mesh.material.specularColor)
            shaderProgram.setUniform("material.hasTexture", 0)
            shaderProgram.setUniform("material.reflectance", item.mesh.material.reflectance)

            // point light
            shaderProgram.setUniform("pointLight.color", pointLight.color)
            shaderProgram.setUniform("pointLight.position", pointLight.position)
            shaderProgram.setUniform("pointLight.intensity", pointLight.intensity)
            shaderProgram.setUniform("pointLight.att.constant", pointLight.constant)
            shaderProgram.setUniform("pointLight.att.linear", pointLight.linear)
            shaderProgram.setUniform("pointLight.att.exponent", pointLight.exponent)

            item.mesh.render {
                shaderProgram.setUniform("texture_sampler", 0)
                shaderProgram.setUniform("specularPower", item.mesh.material.specularPower)
            }
        }
        shaderProgram.unbind()
    }

    fun cleanup() {
        shaderProgram.cleanUp()
    }
}

class BaseLight : IGameLogic {

    val renderer = LightRender()
    val camera = Camera(Vector3f(0f, 1f, 3f))
    var cameraInc = Vector3f()
    val items = ArrayList<GameItem>()

    var forward = false
    var backward = false
    var leftward = false
    var rightward = false

    override fun init(window: Window) {
        renderer.init(window)
        val cube = ObjLoader.loadMesh("/models/bunny.obj")
        cube.material = Material(
            //    texture = TextureCache.getTexture("textures/ball.png")
        )
        val cubeItem = GameItem(cube)
        cubeItem.position.z = -2f
        items.add(cubeItem)
    }

    override fun input(window: Window) {
        cameraInc.set(0f, 0f, 0f)
        when {
            // escape
            window.isKeyPress(GLFW.GLFW_KEY_ESCAPE) -> GLFW.glfwSetWindowShouldClose(window.winHandler, true)

            // 前进
            window.isKeyPress(GLFW.GLFW_KEY_UP) ||
                    window.isKeyPress(GLFW.GLFW_KEY_W) -> {
                cameraInc.z = -1f
                forward = true
                MyLog.info("### forward is $forward")
            }

            window.isKeyUp(GLFW_KEY_UP,forward) ||
                    window.isKeyUp(GLFW_KEY_W,forward) -> {
                forward = false
                MyLog.info("### forward is $forward")
            }

            // 后退
            window.isKeyPress(GLFW.GLFW_KEY_DOWN) ||
                    window.isKeyPress(GLFW.GLFW_KEY_S) -> {
                cameraInc.z = 1f
                backward = true
                MyLog.info("### backward is $backward")
            }
            window.isKeyUp(GLFW_KEY_DOWN,backward) ||
                    window.isKeyUp(GLFW_KEY_S,backward) -> {
                backward = false
                MyLog.info("### backward is $backward")
            }


            // 左
            window.isKeyPress(GLFW.GLFW_KEY_LEFT) ||
                    window.isKeyPress(GLFW.GLFW_KEY_A) -> {
                cameraInc.x = -1f
                leftward = true
                MyLog.info("### leftward is $leftward")
            }
            window.isKeyUp(GLFW_KEY_LEFT,leftward) ||
                    window.isKeyUp(GLFW_KEY_A,leftward) -> {
                leftward = false
                MyLog.info("### leftward is $leftward")
            }

            // 右
            window.isKeyPress(GLFW.GLFW_KEY_RIGHT) ||
                    window.isKeyPress(GLFW.GLFW_KEY_D) -> {
                cameraInc.x = 1f
                rightward = true
                MyLog.info("### rightward is $rightward")
            }

            window.isKeyUp(GLFW_KEY_RIGHT,rightward) ||
                    window.isKeyUp(GLFW_KEY_D,rightward) -> {
                rightward = false
                MyLog.info("### rightward is $rightward")
            }

            // 上
            window.isKeyPress(GLFW.GLFW_KEY_Z) -> cameraInc.y = 1f
            window.isKeyPress(GLFW.GLFW_KEY_X) -> cameraInc.y = -1f
        }
    }

    override fun update(dt: Float) {
        // camera.movePosition(cameraInc.mul(0.1f))
        when{
            forward -> camera.translateZ(-0.1f,true)
            backward -> camera.translateZ(0.1f,true)
            leftward -> camera.translateX(0.1f,true)
            rightward -> camera.translateX(-0.1f,true)
        }
    }

    override fun render(window: Window) {
        if (window.bResize) {
            GL11C.glViewport(0, 0, window.width, window.height)
            window.bResize = false
        }
        GL11C.glClear(GL11C.GL_COLOR_BUFFER_BIT or GL11C.GL_DEPTH_BUFFER_BIT)
        renderer.render(items, camera)
    }

    override fun cleanup() {
        renderer.cleanup()
    }
}

fun main() {
    val window = Window(800, 600, "basic light")
    val engine = Engine(window, BaseLight())
    engine.start()
}